#include "gtest/gtest.h"
#include <traits/spw_type_traits.h>

#include "test.h"
using namespace spwstd;
// 内部元元编程使用工具
// ## or_in
TEST(HelperTest, Or_in) {
    using tru = spwtraits::Or_in<
            spwtraits::Is_Const<const int>,
            spwtraits::Is_Void<void>,
            spwtraits::Is_Lvalue_Reference<int&>
        >;
    using fla = spwtraits::Or_in<
            spwtraits::Is_Const<const int>,
            spwtraits::Is_Void<void>,
            spwtraits::Is_Lvalue_Reference<int&>,
            // false
            spwtraits::Is_Const<const int&>
        >;
    EXPECT_TRUE(tru::value);
    EXPECT_TRUE(fla::value);
    EXPECT_FALSE(spwtraits::Or_in<>::value);
    
    // all false
    using fla2 = spwtraits::Or_in<
            spwtraits::Is_Const<const int&>,
            spwtraits::Is_Void<int>,
            spwtraits::Is_Lvalue_Reference<int>,
            spwtraits::Is_Const<const int&>
        >;
    EXPECT_FALSE(fla2::value);
    // 如果有非法类型
    class A;
    class B;
    class C;
    class D;
    using no = spwtraits::Or_in<spwtraits::Is_Const<const int>,A,B,C,D>;
    EXPECT_TRUE(no::value);
}

TEST(HelperTest, And_in) {
    using tru = spwtraits::And_in<
            spwtraits::Is_Const<const int>,
            spwtraits::Is_Void<void>,
            spwtraits::Is_Lvalue_Reference<int&>
        >;
    using fla = spwtraits::And_in<
            spwtraits::Is_Const<const int>,
            spwtraits::Is_Void<void>,
            spwtraits::Is_Lvalue_Reference<int&>,
            // false
            spwtraits::Is_Const<const int&>
        >;
    EXPECT_TRUE(tru::value);
    EXPECT_FALSE(fla::value);
    EXPECT_TRUE(spwtraits::And_in<>::value);
    
    // all false
    using fla2 = spwtraits::Or_in<
            spwtraits::Is_Const<const int&>,
            spwtraits::Is_Void<int>,
            spwtraits::Is_Lvalue_Reference<int>,
            spwtraits::Is_Const<const int&>
        >;

    EXPECT_FALSE(fla2::value);
}

TEST(HelperTest, Not_in) { 
    EXPECT_TRUE(spwtraits::Not_in<spwtraits::Is_Const<const int&>>::value);
    EXPECT_FALSE(spwtraits::Not_in<spwtraits::Is_Const<const int>>::value);
    using tur = spwtraits::Or_in<spwtraits::Is_Const<int const>,
                spwtraits::Is_Void<void>>;
    EXPECT_FALSE(spwtraits::Not_in<tur>::value);
}

// primary type 
// 类型类别

//TEST(PrimaryTypeTest, IsFinal) {
//class A{};
//class B final{};
//
//    EXPECT_FALSE( spwtraits::Is_Final<A>::value );
//    EXPECT_TRUE( spwtraits::Is_Final<B>::value );
//    EXPECT_FALSE( spwtraits::Is_Final<int>::value );
//    EXPECT_FALSE( spwtraits::Is_Final<A*>::value );
//}

TEST(PrimaryTypeTest, IsClass) {
    class A{};
    struct B{};
    enum class C{};
    union D{};

    EXPECT_TRUE( spwtraits::Is_Class<A>::value );
    EXPECT_TRUE( !spwtraits::Is_Class<A*>::value );
    EXPECT_TRUE( !spwtraits::Is_Class<A&>::value );
    EXPECT_TRUE( spwtraits::Is_Class<B>::value );
    EXPECT_TRUE( !spwtraits::Is_Class<C>::value );
    EXPECT_TRUE( !spwtraits::Is_Class<int>::value );
    // error , 无法区别union
    EXPECT_TRUE( !spwtraits::Is_Class<D>::value ) << "union is not class, but i cant solve. todo solve this error";

}

struct A_ {};    
struct B_ { int m; };
struct C_ { static int m; };
struct D_ { virtual ~D_(); };
union E_ {};
struct G_ {
int:0;  
    // C++ 标准允许“作为特殊情况，无名的长度为零的位域指定下个位域在分配单元边界对齐。
    // 仅当声明无名位域时宽度可为零。”
};

TEST(PrimaryTypeTest, IsEmpty) {
    EXPECT_TRUE( spwtraits::Is_Empty<A_>::value );
    EXPECT_FALSE( spwtraits::Is_Empty<A_*>::value );
    EXPECT_FALSE( spwtraits::Is_Empty<A_&>::value );
    EXPECT_FALSE( spwtraits::Is_Empty<int>::value );
    EXPECT_FALSE( spwtraits::Is_Empty<B_>::value ) << "B_ is " << sizeof(B_);
    EXPECT_TRUE( spwtraits::Is_Empty<C_>::value );
    EXPECT_FALSE( spwtraits::Is_Empty<D_>::value );
    // 无法通过union
    //EXPECT_FALSE( spwtraits::Is_Empty<E_>::value );
    EXPECT_TRUE( spwtraits::Is_Empty<G_>::value );
}
TEST(PrimaryTypeTest, IsVoid) {
    // void
    int a=1;
    EXPECT_FALSE(spwtraits::Is_Void<decltype(a)>::value);
    EXPECT_TRUE(spwtraits::Is_Void<decltype((void)a)>::value);
    EXPECT_TRUE(spwtraits::Is_Void<const void>::value);
    EXPECT_TRUE(spwtraits::Is_Void<const volatile void>::value);
    EXPECT_TRUE(spwtraits::Is_Void<volatile void>::value);
}

TEST(PrimaryTypeTest, IsIntegral) {
    // integral
    EXPECT_TRUE(spwtraits::Is_Integral<bool>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<char>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<signed char>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<unsigned char>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<short>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<unsigned short>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<int>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<unsigned int>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<long>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<unsigned long>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<long long>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<unsigned long long>::value);
    EXPECT_TRUE(spwtraits::Is_Integral<const volatile int>::value);
}

TEST(PrimaryTypeTest, IsFloat) {
    // float
    EXPECT_TRUE(spwtraits::Is_Floating_Point<float>::value);
    EXPECT_TRUE(spwtraits::Is_Floating_Point<double>::value);
    EXPECT_TRUE(spwtraits::Is_Floating_Point<long double>::value);
    EXPECT_TRUE(spwtraits::Is_Floating_Point<const volatile double>::value);
}

TEST(PrimaryTypeTest, IsPoint) {
    // point
    EXPECT_TRUE(spwtraits::Is_Pointer<int*>::value);
    EXPECT_TRUE(spwtraits::Is_Pointer<const volatile int*>::value);
}

#include <container/spw_array.h>
TEST(PrimaryTypeTest, IsArray) {
    int arr[3];
    EXPECT_TRUE(spwtraits::Is_Array<decltype(arr)>::value);
    EXPECT_FALSE(spwtraits::Is_Array<decltype(&arr)>::value);
    EXPECT_TRUE(spwtraits::Is_Array<int[]>::value);
    EXPECT_TRUE(spwtraits::Is_Array<int[3]>::value);
    EXPECT_TRUE(spwtraits::Is_Array<const volatile int[3]>::value);
    EXPECT_FALSE(spwtraits::Is_Array<int*>::value);

    using spw_array = spwarray::array<int, 3>;
    EXPECT_FALSE(spwtraits::Is_Array<spw_array>::value);
}

// isfunction
TEST(PrimaryTypeTest, IsFunction) {
    void test();
    using testc = void() const;
    using testcr = void() const&;
    using testcvr = void() volatile const&;
    using testcrr = void() const&&;
    using testvrr = void() volatile&&;
    EXPECT_TRUE(spwtraits::Is_Function<decltype(test)>::value);
    EXPECT_TRUE(spwtraits::Is_Function<testc>::value);
    EXPECT_TRUE(spwtraits::Is_Function<testcr>::value);
    EXPECT_TRUE(spwtraits::Is_Function<testcrr>::value);
    EXPECT_TRUE(spwtraits::Is_Function<testvrr>::value);
    EXPECT_TRUE(spwtraits::Is_Function<testcvr>::value);
    // 函数指针不是函数类型
    EXPECT_FALSE(spwtraits::Is_Function<decltype(&test)>::value);
    // lamada类型不是函数类型
    auto l = [](){ return 0; };
    EXPECT_FALSE(spwtraits::Is_Function<decltype(l)>::value);
}
// 类型属性
TEST(TypePropertyTest, IsCV) {

    EXPECT_TRUE(spwtraits::Is_Const<const int>::value);
    EXPECT_FALSE(spwtraits::Is_Const<const int*>::value);
    EXPECT_FALSE(spwtraits::Is_Const<const int&>::value);
}

TEST(TypePropertyTest, IsReference) {
    // is lvalue reference
    EXPECT_FALSE(spwtraits::Is_Lvalue_Reference<int>::value);
    EXPECT_TRUE(spwtraits::Is_Lvalue_Reference<int&>::value);
    EXPECT_TRUE(spwtraits::Is_Lvalue_Reference<const int&>::value);
    EXPECT_FALSE(spwtraits::Is_Lvalue_Reference<int&&>::value);

    // is rvalue reference
    EXPECT_FALSE(spwtraits::Is_Rvalue_Reference<int>::value);
    EXPECT_FALSE(spwtraits::Is_Rvalue_Reference<int&>::value);
    EXPECT_FALSE(spwtraits::Is_Rvalue_Reference<const int&>::value);
    EXPECT_TRUE(spwtraits::Is_Rvalue_Reference<int&&>::value);
}

// 类型关系
TEST(TypeRelationTest, IsBaseOf) {
    class A {};
    class B: A{};
    class C {};
    EXPECT_TRUE( (spwtraits::Is_Base_Of<A, B>::value) );
    EXPECT_FALSE( (spwtraits::Is_Base_Of<B, A>::value) );
    EXPECT_FALSE( (spwtraits::Is_Base_Of<C, B>::value) );
    EXPECT_TRUE( (spwtraits::Is_Base_Of<C, C>::value) );
    EXPECT_FALSE( (spwtraits::Is_Base_Of<int, int>::value) );
}
TEST(TypeRelationTest, IsSame) {
    using int_constint = spwtraits::Is_Same<int, const int>;
    EXPECT_FALSE(int_constint::value);
    using rconstint_constint = spwtraits::Is_Same<const int&, const int>;
    EXPECT_FALSE(rconstint_constint::value);

    using sizet_unsignedlong = spwtraits::Is_Same<size_t, unsigned long>;
    EXPECT_TRUE(sizet_unsignedlong::value);

    using arr_arrp = spwtraits::Is_Same<int[3],int*>;
    EXPECT_FALSE(arr_arrp::value);
    
    using arr_stlarr = spwtraits::Is_Same<int[3],spwarray::array<int,3>>;
    EXPECT_FALSE(arr_stlarr::value);
}

// ## isconvertible
// 包含C++主要隐式转换
TEST(TypeRelationTest, IsConvertible) {
    using namespace spwstd::spwtraits;

    //true
    EXPECT_TRUE((Is_Convertible<int*,const int*>::value));
    EXPECT_TRUE((Is_Convertible<int, const int>::value));
    EXPECT_TRUE((Is_Convertible<volatile int, const int>::value));
    EXPECT_TRUE((Is_Convertible<int, float>::value));
    EXPECT_TRUE((Is_Convertible<float, int>::value));
    EXPECT_TRUE((Is_Convertible<double, float>::value));
    EXPECT_TRUE((Is_Convertible<int*, void*>::value));
    EXPECT_TRUE((Is_Convertible<int[4], int*>::value));
    EXPECT_TRUE((Is_Convertible<float&, int>::value));
    EXPECT_TRUE((Is_Convertible<int, const int&>::value));
    EXPECT_TRUE((Is_Convertible<const int, const int&>::value));
    EXPECT_TRUE((Is_Convertible<const int&, int>::value));
    EXPECT_TRUE((Is_Convertible<float, const int&>::value));
    EXPECT_TRUE((Is_Convertible<int(int), int(*)(int)>::value));
    EXPECT_TRUE((Is_Convertible<int(&)(int), int(*)(int)>::value));
    EXPECT_TRUE((Is_Convertible<int(int), int(&)(int)>::value));
    EXPECT_TRUE((Is_Convertible<ClassType, ClassType>::value));
    EXPECT_TRUE((Is_Convertible<DerivedType, ClassType>::value));
    EXPECT_TRUE((Is_Convertible<DerivedType*, ClassType*>::value));
    EXPECT_TRUE((Is_Convertible<DerivedType&, ClassType&>::value));

    EXPECT_TRUE((Is_Convertible<void, void>::value));
    EXPECT_TRUE((Is_Convertible<const void, void>::value));
    EXPECT_TRUE((Is_Convertible<void, volatile void>::value));

    EXPECT_TRUE((Is_Convertible<double&, ExplicitClass>::value));

    //false
    EXPECT_FALSE((Is_Convertible<const int*, int*>::value));
    // 会产生临时变量，临时变量不可以赋值给非const和右值变量
    EXPECT_FALSE((Is_Convertible<int*, float*>::value));
    EXPECT_FALSE((Is_Convertible<float*, int*>::value));
    EXPECT_FALSE((Is_Convertible<void*, int*>::value));

    EXPECT_FALSE((Is_Convertible<const int[4], int*>::value));
    EXPECT_FALSE((Is_Convertible<int[4], int[4]>::value));

    EXPECT_FALSE((Is_Convertible<const int&, int&>::value));
    EXPECT_FALSE((Is_Convertible<float&, int&>::value));
    EXPECT_FALSE((Is_Convertible<float, volatile int&>::value));

    EXPECT_FALSE((Is_Convertible<int(int), int(int)>::value));
    EXPECT_FALSE((Is_Convertible<int(int), int(*)(void)>::value));
    EXPECT_FALSE((Is_Convertible<int(*)(int), int(&)(int)>::value));

    EXPECT_FALSE((Is_Convertible<int, EnumType>::value));
    EXPECT_FALSE((Is_Convertible<int, ClassType>::value));
    EXPECT_FALSE((Is_Convertible<ClassType, DerivedType>::value));
    EXPECT_FALSE((Is_Convertible<ClassType*, DerivedType*>::value));
    EXPECT_FALSE((Is_Convertible<ClassType&, DerivedType&>::value));
}

// 类型修改
TEST(TypeModifyTest, Remove) {
// remove const/volatie
    EXPECT_FALSE(spwtraits::Is_Const<spwtraits::Remove_Const<const int>::type>::value);
    EXPECT_FALSE(spwtraits::Is_Volatile<spwtraits::Remove_Volatile<volatile int>::type>::value);
    EXPECT_FALSE(spwtraits::Is_Const<spwtraits::Remove_Cv<const volatile int>::type>::value);
    EXPECT_FALSE(spwtraits::Is_Volatile<spwtraits::Remove_Cv<const volatile int>::type>::value);
    EXPECT_TRUE((spwtraits::Is_Same<spwtraits::Remove_Cv<const volatile int>::type, int>::value));
    EXPECT_TRUE((spwtraits::Is_Same<spwtraits::Remove_Cv<const int>::type, int>::value));
    EXPECT_TRUE((spwtraits::Is_Same<spwtraits::Remove_Cv<volatile int>::type, int>::value));
    EXPECT_FALSE(spwtraits::Is_Const<
            spwtraits::Remove_Reference<
                spwtraits::Remove_Const<const int&>::type
            >
            >::value);

// remove reference
    EXPECT_TRUE(spwtraits::Is_Const<spwtraits::Remove_Reference<const int&>::type>::value);
    EXPECT_TRUE(spwtraits::Is_Const<spwtraits::Remove_Reference<const int&&>::type>::value);
    EXPECT_TRUE(spwtraits::Is_Const<spwtraits::Remove_Reference<const int>::type>::value);

// remove pointer
    EXPECT_FALSE(spwtraits::Is_Pointer<spwtraits::Remove_Pointer<const int*>>::value);
    EXPECT_FALSE(spwtraits::Is_Pointer<spwtraits::Remove_Pointer<int*>>::value);
    EXPECT_TRUE( (spwtraits::Is_Same<spwtraits::Remove_Pointer<const int*>::type, const int>::value) );
    EXPECT_TRUE( (spwtraits::Is_Same<spwtraits::Remove_Pointer<int*>::type, int>::value) );
    EXPECT_TRUE( (spwtraits::Is_Same<spwtraits::Remove_Pointer<const int* const volatile>::type, const int>::value) );

    // remove 数组一个维度
    EXPECT_TRUE( (spwtraits::Is_Same<spwtraits::Remove_Extent<int[3]>::type, int>::value) );
    EXPECT_TRUE( (spwtraits::Is_Same<spwtraits::Remove_Extent<const int[3]>::type,const int>::value) );
    EXPECT_TRUE( (spwtraits::Is_Same<spwtraits::Remove_Extent<int[3][2]>::type, int[2]>::value) );
}

TEST(TypeModifyTest, Add) {
    EXPECT_TRUE(spwtraits::Is_Const<spwtraits::Add_Const<int>::type>::value);
    EXPECT_TRUE(spwtraits::Is_Lvalue_Reference<spwtraits::Add_Con_ref<int>::type>::value);
    EXPECT_TRUE(spwtraits::Is_Const<spwtraits::Remove_Reference<spwtraits::Add_Con_ref<int>::type>::type>::value);
    EXPECT_TRUE(spwtraits::Is_Lvalue_Reference<spwtraits::Add_Con_ref<int&>::type>::value);
    EXPECT_TRUE(spwtraits::Is_Const<spwtraits::Remove_Reference<spwtraits::Add_Con_ref<int&>::type>::type>::value);
    EXPECT_TRUE(spwtraits::Is_Lvalue_Reference<spwtraits::Add_Con_ref<const int&>::type>::value);
    EXPECT_TRUE(spwtraits::Is_Const<spwtraits::Remove_Reference<spwtraits::Add_Con_ref<const int&>::type>::type>::value);
}

// 一些模板
TEST(TypeTemplateTest, Enable_if) {
    using enableifone = spwtraits::Is_Same<
        const int&, 
        spwtraits::Enable_If<
            spwtraits::Is_Lvalue_Reference<const int&>::value, 
            const int&
        >::type
    >;

    using enableiftwo = spwtraits::Is_Void<
        spwtraits::Enable_If<
            spwtraits::Is_Lvalue_Reference<const int&>::value, void
        >::type
    >;

    EXPECT_TRUE(enableifone::value);
    EXPECT_TRUE(enableiftwo::value);
}

TEST(TypeTemplateTest, Conditional) {
    using condtrue = spwtraits::Conditional<true, const int&, int&&>;
    using condfalse = spwtraits::Conditional<false, const int&, int&&>;

    EXPECT_TRUE(spwtraits::Is_Lvalue_Reference<condtrue::type>::value);
    EXPECT_TRUE(spwtraits::Is_Rvalue_Reference<condfalse::type>::value);
}

// declval 使用
// 可以不求值，返回T&& 
struct NonDefault
{
    NonDefault() = delete;
    int foo() const { return 1; }
};

TEST(TypeTemplateTest, Declval) {
    // 不求值
    using T1 = decltype(spwtraits::Declval<NonDefault>().foo());
    testing::StaticAssertTypeEq<T1, int>();
    // 错误NonDefault没有默认构造函数
    // using t2 = decltype(NonDefault().foo());

    // 返回T&&, 保留CV
    testing::StaticAssertTypeEq<decltype(spwtraits::Declval<int&>()), int&>();
    testing::StaticAssertTypeEq<decltype(spwtraits::Declval<int>()), int&&>();
    testing::StaticAssertTypeEq<decltype(spwtraits::Declval<const int>()), const int&&>();
}

// replace template first arg
template<typename Fir, typename Sec, typename thr>
struct Replace_test{
    using ty1 = Fir;
    using ty2 = Sec;
    using ty3 = thr;
};

TEST(ReplaceTest, TempFirstArg) {
    using a = spwtraits::ReplaceFirstArg<Replace_test<const int, int, double>, double>::type; 
    EXPECT_TRUE( (spwtraits::Is_Same<a::ty1, double>::value) );

    using b = spwtraits::ReplaceFirstArg<Replace_test<const int, int, double>, const double>::type; 
    EXPECT_TRUE( (spwtraits::Is_Same<b::ty1, const double>::value) );
}

